package com.aletto.demo_android_web3j.utils;

import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Event;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.contracts.token.ERC20Interface;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.RemoteCall;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.exceptions.TransactionException;
import org.web3j.tx.Contract;
import org.web3j.tx.TransactionManager;
import org.web3j.tx.gas.ContractGasProvider;

import org.web3j.protocol.core.methods.response.MinerStartResponse;


import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;

import io.reactivex.Flowable;

public class ERC20 extends Contract implements ERC20Interface<ERC20.ApprovalEventResponse, ERC20.TransferEventResponse> {

    public static final String BINARY         = null;
    public static final String FUNC_ALLOWANCE = "allowance";

    public static final String FUNC_BALANCE_OF = "balanceOf";

    public static final String FUNC_NAME = "name";

    public static final String FUNC_STANDARD = "standard";

    public static final String FUNC_SYMBOL = "symbol";

    public static final String FUNC_TOTAL_SUPPLY = "totalSupply";

    public static final String FUNC_TRANSFER = "transfer";

    public static final String FUNC_APPROVE = "approve";

    public static final String FUNC_TRANSFER_FROM = "transferFrom";

    public static final Event APPROVAL_EVENT = new Event(
            "Approval",
            Arrays.asList(
                    new TypeReference<Address>(true) {
                        // ...
                    },
                    new TypeReference<Address>(true) {
                        // ...
                    },
                    new TypeReference<Uint256>() {
                        // ...
                    }
            )
    );

    public static final Event TRANSFER_EVENT = new Event(
            "Transfer",
            Arrays.asList(
                    new TypeReference<Address>(true) {
                        // ...
                    },
                    new TypeReference<Address>(true) {
                        // ...
                    },
                    new TypeReference<Uint256>() {
                        // ...
                    })
    );


    protected static final HashMap<String, String> _addresses;

    static {
        _addresses = new HashMap<>();
    }


    protected ERC20(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
        super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider);
    }

    protected ERC20(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
        super(BINARY, contractAddress, web3j, credentials, contractGasProvider);
    }

    @Deprecated
    protected ERC20(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
        super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit);
    }

    @Deprecated
    protected ERC20(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
        super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit);
    }

    @Override
    public RemoteCall<BigInteger> allowance(String owner, String spender) {
        return executeRemoteCallSingleValueReturn(
                new Function(
                        FUNC_ALLOWANCE,
                        Arrays.asList(
                                new Address(owner),
                                new Address(spender)
                        ),
                        Collections.singletonList(new TypeReference<Uint256>() {
                            // 泛型不可省略
                        })
                ),
                BigInteger.class
        );
    }


    @Override
    public RemoteCall<TransactionReceipt> approve(String spender, BigInteger value) {
        return executeRemoteCallTransaction(
                new Function(
                        FUNC_APPROVE,
                        Arrays.asList(
                                new Address(spender),
                                new Uint256(value)
                        ),
                        Collections.emptyList()
                )
        );
    }

    public TransactionReceipt _approve(String spender, BigInteger value) throws TransactionException, IOException {
        return executeTransaction(
                new Function(
                        FUNC_APPROVE,
                        Arrays.asList(
                                new Address(spender),
                                new Uint256(value)
                        ),
                        Collections.emptyList()
                )
        );
    }

    @Override
    public RemoteCall<TransactionReceipt> transferFrom(String from, String to, BigInteger value) {
        return executeRemoteCallTransaction(
                new Function(
                        FUNC_TRANSFER_FROM,
                        Arrays.asList(
                                new Address(from),
                                new Address(to),
                                new Uint256(value)
                        ),
                        Collections.emptyList())
        );
    }

    @Override
    public List<ApprovalEventResponse> getApprovalEvents(TransactionReceipt transactionReceipt) {
//        List<Contract.EventValuesWithLog> valueList = extractEventParametersWithLog("getApprovalEvents", transactionReceipt);
//        ArrayList<ApprovalEventResponse> responses = new ArrayList<ApprovalEventResponse>(valueList.size());
//        for (Contract.EventValuesWithLog eventValues : valueList) {
//            ApprovalEventResponse typedResponse = new ApprovalEventResponse();
//            typedResponse.log = eventValues.getLog();
//            typedResponse._owner = (String) eventValues.getIndexedValues().get(0).getValue();
//            typedResponse._spender = (String) eventValues.getIndexedValues().get(1).getValue();
//            typedResponse._value = (BigInteger) eventValues.getNonIndexedValues().get(0).getValue();
//            responses.add(typedResponse);
//        }
//        return responses;

        return null;
    }

    @Override
    public Flowable<ApprovalEventResponse> approvalEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) {
        return null;
    }

    @Override
    public RemoteCall<BigInteger> totalSupply() {
        return executeRemoteCallSingleValueReturn(
                new Function(
                        FUNC_TOTAL_SUPPLY,
                        Collections.emptyList(),
                        Collections.singletonList(new TypeReference<Uint256>() {
                            // ...
                        })
                ),
                BigInteger.class
        );
    }

    @Override
    public RemoteCall<BigInteger> balanceOf(String who) {
        return executeRemoteCallSingleValueReturn(
                new Function(
                        FUNC_BALANCE_OF,
                        Collections.singletonList(new Address(who)),
                        Collections.singletonList(new TypeReference<Uint256>() {
                            // ...
                        })
                ),
                BigInteger.class
        );
    }

    public RemoteCall<TransactionReceipt> transfer(String to, BigInteger value) {
        return executeRemoteCallTransaction(
                new org.web3j.abi.datatypes.Function(
                        FUNC_TRANSFER,
                        Arrays.asList(
                                new Address(to),
                                new Uint256(value)
                        ),
                        Collections.emptyList()
                )
        );
    }

    @Override
    public List<TransferEventResponse> getTransferEvents(TransactionReceipt transactionReceipt) {
        return null;
    }

    @Override
    public Flowable<TransferEventResponse> transferEventFlowable(DefaultBlockParameter startBlock, DefaultBlockParameter endBlock) {
        return null;
    }

    public RemoteCall<String> name() {
        return executeRemoteCallSingleValueReturn(
                new org.web3j.abi.datatypes.Function(FUNC_NAME,
                        Collections.emptyList(),
                        Collections.singletonList(new TypeReference<Utf8String>() {
                            // ...
                        })
                ),
                String.class
        );
    }

    public RemoteCall<String> standard() {
        return executeRemoteCallSingleValueReturn(
                new Function(
                        FUNC_STANDARD,
                        Collections.emptyList(),
                        Collections.singletonList(new TypeReference<Utf8String>() {
                            // ...
                        })
                ),
                String.class
        );
    }

    public RemoteCall<String> symbol() {
        return executeRemoteCallSingleValueReturn(
                new Function(
                        FUNC_SYMBOL,
                        Collections.emptyList(),
                        Collections.singletonList(new TypeReference<Utf8String>() {
                            // ...
                        })
                ),
                String.class
        );
    }

    public static ERC20 load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
        return new ERC20(contractAddress, web3j, credentials, contractGasProvider);
    }

    public static ERC20 load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
        return new ERC20(contractAddress, web3j, transactionManager, contractGasProvider);
    }

    @Deprecated
    public static ERC20 load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
        return new ERC20(contractAddress, web3j, credentials, gasPrice, gasLimit);
    }

    @Deprecated
    public static ERC20 load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
        return new ERC20(contractAddress, web3j, transactionManager, gasPrice, gasLimit);
    }

    public static RemoteCall<ERC20> deploy(Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider, BigInteger _initialSupply) {
        String encodedConstructor = FunctionEncoder.encodeConstructor(Collections.singletonList(new Uint256(_initialSupply)));
        return deployRemoteCall(ERC20.class, web3j, credentials, contractGasProvider, BINARY, encodedConstructor);
    }

    public static RemoteCall<ERC20> deploy(Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider, BigInteger _initialSupply) {
        String encodedConstructor = FunctionEncoder.encodeConstructor(Collections.singletonList(new Uint256(_initialSupply)));
        return deployRemoteCall(ERC20.class, web3j, transactionManager, contractGasProvider, BINARY, encodedConstructor);
    }

    @Deprecated
    public static RemoteCall<ERC20> deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit, BigInteger _initialSupply) {
        String encodedConstructor = FunctionEncoder.encodeConstructor(Collections.singletonList(new Uint256(_initialSupply)));
        return deployRemoteCall(ERC20.class, web3j, credentials, gasPrice, gasLimit, BINARY, encodedConstructor);
    }

    @Deprecated
    public static RemoteCall<ERC20> deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit, BigInteger _initialSupply) {
        String encodedConstructor = FunctionEncoder.encodeConstructor(Collections.singletonList(new Uint256(_initialSupply)));
        return deployRemoteCall(ERC20.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, encodedConstructor);
    }

    protected String getStaticDeployedAddress(String networkId) {
        return _addresses.get(networkId);
    }

    public static String getPreviouslyDeployedAddress(String networkId) {
        return _addresses.get(networkId);
    }

    public static class ApprovalEventResponse extends MinerStartResponse {
        public String owner;

        public String spender;

        public BigInteger value;
    }

    public static class TransferEventResponse extends MinerStartResponse {
        public String from;

        public String to;

        public BigInteger value;
    }

    public static class F {

        public static Function allowance(String owner, String spender) {
            return new Function(
                    FUNC_ALLOWANCE,
                    Arrays.asList(
                            new org.web3j.abi.datatypes.Address(owner),
                            new org.web3j.abi.datatypes.Address(spender)
                    ),
                    Collections.singletonList(new TypeReference<Uint256>() {
                        // 泛型不可省略
                    })
            );
        }

        public static Function approve(String spender, BigInteger value) {
            return new Function(
                    FUNC_APPROVE,
                    Arrays.asList(
                            new Address(spender),
                            new Uint256(value)
                    ),
                    Collections.emptyList()
            );
        }

        public static Function balanceOf(String who) {
            return new Function(
                    FUNC_BALANCE_OF,
                    Collections.singletonList(new Address(who)),
                    Collections.singletonList(new TypeReference<Uint256>() {
                        // ...
                    })
            );
        }


    }

}
